#!/usr/bin/env python

import re
import time
import serial

class Modem(serial.Serial):
    '''Model class

    @author Julot <andy.julot@gmail.com>
    @since Friday, February 17, 2012. 09:58 AM'''
    
    def __init__(self, *args, **kwargs):
        serial.Serial.__init__(self, *args, **kwargs)

    @classmethod
    def search(cls):
        '''Search modem.

        @return Modem object or None if not found.'''

        modem = None

        # @constant tuple baudrate for serial communication.
        baudrates = (115200, 57600, 38400, 19200, 9600, 4800, 2400, 1800, 1200)

        for port in range(1, 257):
            for baudrate in baudrates:
                if modem: modem.close()
                try:
                    modem = cls('COM%s' % port, baudrate=baudrate)
                    if modem.isExists:
                        s = 'Modem found on COM%s at %sbps'
                        return modem
                except: pass
        return None
    
    @property
    def isExist(self):
        '''Get property of exist.

        @return boolean wether the modem existed.'''

        return self.hasOk(self.at('e0'))

    def hasOk(self, value):
        '''Check value for string OK.

        @param string response from modem.
        @return boolean wether value contains OK or not.'''

        return 'OK' in value
    
    def at(self, command, timeOut=7, prefixed=True, postfixed=True):
        '''Send command to modem.

        @param string at command.
        @param integer timeout in second.
        @param boolean whether using AT prefix or not.
        @return string modem response.'''

        try:
            self.flush()
            if prefixed: command = 'AT' + command
            if postfixed: command += '\n'
##            print '>>> %s' % command.strip()
            self.write(command)
            value = ''
            startTime = time.time()
            while True:
                time.sleep(.1)
                inWaiting = self.inWaiting()
                if inWaiting: value += self.read(inWaiting)
                if value.find('>') >= 0: break
                if value.find('OK') >= 0: break
                if value.find('ERROR') >= 0: break
                if (time.time() - startTime) > timeOut: break
        except: value = ''
##        print '<<< %s' % value.strip()
        return value

    def e1(self):
        '''Turn echo on.

        @return boolean wether operation succeeds.'''

        return self.hasOk(self.at('e1'))

    def e0(self):
        '''Turn echo off.

        @return boolean wether operation succeeds.'''

        return self.hasOk(self.at('e0'))

    def cpms(self, order='?'):
        '''Change message storage.

        @param string message storage order.
        @return boolean wether operation succeeds.'''
        
        response = self.at('+CPMS=%s' % order)
        
        if order != '?': return self.hasOk(response)
    
        pattern = '\+CPMS: (.*)'
        results = re.findall(pattern, response)
        if results:
            result = results[0]
            result = result.strip()
            pattern = '\(([^(^)]*)\)'
            return re.findall(pattern, response)

    def cmgf(self, mode=0):
        '''Change mode to PDU or Text.

        @param integer PDU mode. 0 = PDU mode, 1 = Text mode
        @return boolean wether mode changed.'''

        return self.hasOk(self.at('+CMGF=%s' % mode))

    def cmgl(self, status=4):
        '''Return dictionary of index->message from modem in pdu format.
        index is position of message in modem, so it valid for other modem
        operation such as delete.

        @param integer if mode = 0, string if mode = 1. See changeMode method.
        @return dict of index, message.'''

        self.flushInput()
        result = self.at('+CMGL=%s' % status, 20)
        results = [x.strip() for x in result.split('\n')]
        result = '\n'.join(results)
        pattern = '\+CMGL:([\s\d]*),([\s\d]*),([\s\d]*),([\s\d]*)\n'
        pattern += '([0-9a-fA-F]+)'
        results = re.findall(pattern, result)
        messages = {}
        for result in results:
            try: index = int(result[0].strip())
            except: continue
            messages[index] = result[4].strip()
        return messages

    def cmgd(self, index):
        '''Delete message from modem.

        @param integer index of message in modem.
        @return boolean wether operation succeeds.'''

        return self.hasOk(self.at('+CMGD=%s' % index))
    
    def cusd(self, number):
        self.flush()
        self.write('AT+CUSD=1,"%s",15\n' % number)
        result = ''
        startTime = time.time()
        ok = False
        cusd = False
        while True:
            time.sleep(.1)
            result += self.readline()
            if result.find('ERROR') >= 0: break
            if result.find('+CUSD') >= 0: cusd = True
            if result.find('OK') >= 0: ok = True
            if (ok and cusd) or (time.time() - startTime) > 20: break
        
        if not (ok and cusd): return None
        
        results = [x.strip() for x in result.split('\n')]
        result = '\n'.join(results)
        pattern = r'\+CUSD:[\s\d]*,"(.*)",[\s\d]*'
        results = re.findall(pattern, result)
        return results[0] if len(results) else None

if __name__ == '__main__':
    modem = Modem('COM5')
    print modem.echoOff()